import React, {
  Component,
  PureComponent,
  createContext,
  useContext,
} from 'react';
import storage, { CURRENT_LANG } from '@/utils/storage';
import type { LangEnum } from './typing';
import type { Langs } from './index';
import I18N from './index';

export interface I18NProps {
  I18N: Langs;
  setLangTriggerRender: (lang: LangEnum) => void;
}

export const I18NContext = createContext({
  I18N,
  setLangTriggerRender: (_: LangEnum) => {},
});

/**
 * 为应用提供国际化的上下文，
 * 并包装应用的其他部分，
 * 使它们能够轻松地访问到与国际化相关的信息
 */
export class I18NContextWrapper extends Component<
  React.PropsWithChildren<{}>,
  {
    I18N: Langs;
    setLangTriggerRender: (lang: LangEnum) => void;
  }
> {
  public constructor(props: any) {
    super(props);
    this.state = {
      I18N,
      setLangTriggerRender: this.setLang,
    };
  }

  public setLang = (lang: LangEnum) => {
    if (lang === I18N.currentLang) {
      return;
    }
    I18N.setLangHandle(lang);
    storage.setStorage(CURRENT_LANG, lang);
    this.setState((state: any) => ({
      I18N: state.I18N,
    }));
  };

  public render() {
    return (
      <I18NContext.Provider key={I18N.currentLang} value={this.state}>
        {this.props.children}
      </I18NContext.Provider>
    );
  }
}

/**
 * HOC 高阶组件函数
 * 将 I18N 和 setLangTriggerRender 这两个属性注入到
 * WrappedComponent 中，使 WrappedComponent
 * 具备了访问国际化上下文的能力
 *
 * PureComponent 浅比较来决定是否重新渲染
 * @param WrappedComponent 组件
 */
export const I18NHOC: (...args: any) => any = (WrappedComponent) =>
  class WrapperComponent extends PureComponent {
    public render() {
      return (
        // Consumer 订阅 I18NContext 上下文的变化
        <I18NContext.Consumer>
          {({ I18N: _I18N, setLangTriggerRender }) => (
            <WrappedComponent
              I18N={_I18N}
              setLangTriggerRender={setLangTriggerRender}
              {...this.props}
            />
          )}
        </I18NContext.Consumer>
      );
    }
  };

/**
 * 自定义 hook
 * useContext Hook 来获取 I18NContext 上下文的值，
 * 并将其返回
 */
export const useI18n = () => {
  const context = useContext(I18NContext);
  return context;
};
